package com.lxx.wiki.config;

import com.lxx.wiki.filter.JwtAuthenticationTokenFilter;
import com.lxx.wiki.whitelist.WhiteList;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.access.intercept.AuthorizationFilter;

@Configuration
public class SecurityConfiguration {

    @Autowired
    private WhiteList whiteList;


    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;

    @Autowired
    private AuthenticationEntryPoint authenticationEntryPoint;

    @Autowired
    private AccessDeniedHandler accessDeniedHandler;

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        // 自定义配置
        http.authorizeHttpRequests(auth -> auth
                        .requestMatchers(whiteList.getRequestMatchers()).permitAll()
                        // 除上面外的所有请求需要鉴权认证
                        .anyRequest().authenticated())
                .csrf(csrf -> csrf.disable()) // 自定义的登录请求Post请求，需要禁用csrf保护，否则不支持前端发起的Post请求
                .sessionManagement(session -> session.disable()) // 前后端分离是无状态的，不需要session了，直接禁用。
                .formLogin(form -> form.disable())  // 禁用内置的登录表单后，相关过滤器都不会再起作用了
                .logout(logout -> logout.disable()) // 禁用默认的登出过滤器
                .addFilterBefore(jwtAuthenticationTokenFilter, AuthorizationFilter.class)// 添加过滤器,AuthorizationFilter是内置的鉴权过滤器，是整个过滤器链的最后一个过滤器
                // 注入异常处理器
                .exceptionHandling(handler -> handler.accessDeniedHandler(accessDeniedHandler)) // 授权异常
                .exceptionHandling(entryPoint -> entryPoint.authenticationEntryPoint(authenticationEntryPoint)); // 认证异常

        // 返回新的过滤器链
        return http.build();
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }


    // 登录时需要调用AuthenticationManager.authenticate进行用户验证
    @Bean
    public AuthenticationManager authenticationManager(PasswordEncoder passwordEncoder) throws Exception {
        DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
        provider.setUserDetailsService(userDetailsService);
        provider.setPasswordEncoder(passwordEncoder);
        return new ProviderManager(provider);
        /**
         * AuthenticationManager的实现类ProviderManager调用authenticate()方法进行认证
         * providerManager.authenticate()调用AuthenticationProvider的实现类DaoAuthenticationProvider调用retrieveUser()方法，
         * retrieveUser()方法调用DaoAuthenticationProvider的成员userDetailsService的loadUserByUsername()方法
         *  UserDetails loadedUser = this.getUserDetailsService().loadUserByUsername(username);
         * 即
         * ProviderManager(AuthenticationManager的实现类)需要DaoAuthenticationProvider(AuthenticationProvider);
         * DaoAuthenticationProvider需要userDetailsService。
         */
    }

}
